/*
 * Copyright 2013 Robert Peszek.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package fpig.io.functions

import fpig.io.structures.FunListInputStream;
import fpig.lists.functions.Base;
import fpig.lists.structures.FunList
import fpig.lists.structures.LazyList
import fpig.util.CallUtil
import groovy.lang.Closure;

/**
* Functions for moving in and out between I/O and FunList (Lazy List)
* The FunList/LazyList is persisting data on first retrieval so calling list.tail several times will not cause several calls to tokenizer
* 
*
* http://code.google.com/p/fpiglet/
* @author Robert Peszek
*/

class InAndOutOfFunLists {
    
	//UGLY imperative code alert
	static Closure universalTokenizer = CallUtil.toFunction {delimiter, is ->
		def buffer = new StringBuffer();
		int read = is.read()
		while(read !=-1 && read as char!=delimiter as char){
			buffer.append(read as char)
			read = is.read()
		}
		if(read==-1){
			if(buffer.size() == 0)
				return null
		} 
		buffer.toString()
	}

	//TODO needs handling of encoding
	static Closure universalUntokenizer = CallUtil.toFunction {delimiter, s->
		s = s + delimiter
		//new ReaderInputStream(new StringReader(s))
		new ByteArrayInputStream(s.getBytes()) //TODO handle encoding!
	}

    static Closure getFunlistInC() {
		return {Closure tokenizer, data, inputStream->
			if(data!=null){
				//persisting closure, this is designed not to call tokenizer more than once
				//this poor man's memoization is more than about performance, it removes side-effects
				Closure tailC = {
					def last = myHead 
					if(myHead == null){
					   myHead = tokenizer(inputStream); 
    				   myTail = InAndOutOfFunLists.funlistInC(tokenizer, myHead, inputStream)
					}
					myTail
				}
				tailC.delegate = [:]
				FunList.EMPTYLIST.build(data, tailC)
			} else {
			    //inputStream.close() //have client close it, that looks to me like a side-effect
			    FunList.EMPTYLIST
			}
	    }
    }
	static Closure funlistIn = CallUtil.toFunction {Closure tokenizer, inputStream->
		def first = tokenizer(inputStream)
		InAndOutOfFunLists.funlistInC(tokenizer, first, inputStream)
	}
	
    static Closure funlistOutC = {Closure untokenizer, FunList flist ->
		new FunListInputStream(untokenizer, flist); 
    }
    static Closure funlistOut = CallUtil.toFunction funlistOutC
	
	static Closure withFunList = CallUtil.toFunction {Closure tokenizer, Closure untokenizer, Closure f, InputStream s ->
		def output = f << InAndOutOfFunLists.funlistIn(tokenizer) << s
		if(output instanceof FunList)
		   InAndOutOfFunLists.funlistOut(untokenizer) << output
		else 
		   output
	}
	
	static Closure withFunListUsingToken = CallUtil.toFunction {token, Closure f, InputStream s  -> 
		Closure tokenizer  = InAndOutOfFunLists.universalTokenizer(token)
		Closure untokenizer = InAndOutOfFunLists.universalUntokenizer(token)
		InAndOutOfFunLists.withFunList(tokenizer, untokenizer, f, s)
	}
}
